package com.google.code.mircle.core.transfers;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.swing.DefaultListModel;

import org.apache.commons.lang3.StringUtils;

import com.google.code.mircle.core.Core;
import com.google.code.mircle.core.Model;
import com.google.code.mircle.core.Worker;
import com.google.code.mircle.core.Worker.DownloadState;
import com.google.code.mircle.core.Worker.State;

public class DirectModel implements Model {

    String name = "Direct";

    DefaultListModel list = new DefaultListModel();

    Core core;
    TransfersPlugin p;

    // count current downloads
    int count = 0;
    // max amount of concurent downloads
    int countMax = 3;

    public DirectModel(Core core, TransfersPlugin p) {
        this.core = core;
        this.p = p;
    }

    public Object save() {
        ArrayList<Object[]> list = new ArrayList<Object[]>();

        for (Enumeration<?> e = this.list.elements(); e.hasMoreElements();) {
            Transfer t = (Transfer) e.nextElement();
            list.add(new Object[] { t.getWorker().getClass().getName(), t.getWorker().save() });
        }

        return list;
    }

    public void load(Object o) {
        try {
            @SuppressWarnings("unchecked")
            ArrayList<Object[]> list = (ArrayList<Object[]>) o;
            for (Object[] oo : list) {
                Worker w = null;

                String name = (String) oo[0];
                Worker.State state = (State) oo[1];

                Class<?> klass = Class.forName((String) name);
                w = (Worker) klass.newInstance();
                w.load(state);

                Transfer t = new Transfer(core, p, w);
                this.list.addElement(t);

                DownloadState ds = w.getDownloadState();
                if (ds == null)
                    ds = DownloadState.DOWNLOAD_QUEUED;

                switch (ds) {
                case DOWNLOAD_START:
                case DOWNLOAD_QUEUED:
                    start(t);
                }
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * try to start download or place it to the download queue
     * 
     * @param t
     */
    public void start(Transfer t) {
        if (count >= countMax) {
            t.getWorker().queued();
            return;
        }

        t.addListener(new Transfer.Listener() {
            @Override
            public void stop(Transfer t) {
                Core.invoke(new Runnable() {
                    @Override
                    public void run() {
                        count--;
                        startNext();
                    }
                });
            }

            @Override
            public void start(Transfer t) {
            }

            @Override
            public void changed(Transfer t) {
            }
        });

        count++;
        t.start();
    }

    public void startNext() {
        for (Enumeration<?> e = list.elements(); e.hasMoreElements();) {
            Transfer t = (Transfer) e.nextElement();

            if (t.getWorker().getDownloadState() == DownloadState.DOWNLOAD_QUEUED) {
                start(t);

                // stop starting tasks only if here is a limit
                if (count >= countMax)
                    return;
            }
        }
    }

    public void add(Transfer t) {
        list.addElement(t);
    }

    public void del(Transfer t) {
        list.removeElement(t);
    }

    public String toString() {
        return name;
    }

    public DefaultListModel getTransfers() {
        return list;
    }

    @Override
    public void close() {
        for (Enumeration<?> e = list.elements(); e.hasMoreElements();) {
            Transfer t = (Transfer) e.nextElement();
            t.close();
        }
    }

    Worker getWorker(String url) {
        try {

            // [url:some_url][origin:origin_url]
            {
                String uuu = null;
                String origin = null;

                String q;
                try {
                    q = URLDecoder.decode(url, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }

                String[] uu = StringUtils.splitByWholeSeparator(q, "][");
                for (String u : uu) {
                    u = StringUtils.removeStart(u, "[");
                    u = StringUtils.removeEnd(u, "]");
                    if (u.startsWith("url:")) {
                        uuu = StringUtils.removeStart(u, "url:");
                    }
                    if (u.startsWith("origin:")) {
                        origin = StringUtils.removeStart(u, "origin:");
                    }
                }

                if (uuu != null && origin != null) {
                    return new DirectDownload(new URL(uuu), p.downloadFolder, origin);
                }
            }

            if (VGetDownload.handle(url))
                return new VGetDownload(new URL(url), p.downloadFolder);

            if (EchoRuDownload.handle(url))
                return new EchoRuDownload(new URL(url), p.downloadFolder);

            if (TorrentDownload.handle(url))
                return new TorrentDownload(url, p.downloadFolder);

            return new DirectDownload(new URL(url), p.downloadFolder);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    Transfer check(Worker w) {
        for (Enumeration<?> e = list.elements(); e.hasMoreElements();) {
            Transfer t = (Transfer) e.nextElement();

            if (t.getWorker().checkSource(w))
                return t;

            if (t.getWorker().checkTarget(w))
                throw new RuntimeException("conflicting target download folder / target content");
        }

        return null;
    }

    public Transfer download(String url) {
        Worker w;

        // url can come with mircle: prefix. usially from web browser. when user
        // click on special link
        url = StringUtils.removeStart(url, "mircle:");

        w = getWorker(url);

        Transfer t;

        t = check(w);
        if (t != null)
            return t;

        t = new Transfer(core, p, w);
        add(t);

        core.save(p);

        return t;
    }
}
